#define __USE_MALLOC

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <vector>
#include <thread>
#include <sstream>

#include <string.h>
#include <time.h>

#include "include/string_intern.h"

#include <windows.h>
#include <psapi.h>
#pragma comment(lib,"psapi.lib")

using namespace std;

const int buffersize = 4096;
// const int get_count = 1000000;
const int internsize = 200000;

rs::stringintern::StringReference* refs;
rs::stringintern::StringIntern* intern;

bool GetFileWords(const char* filename, string words[]) {
    const auto delims = " \t\r\n\f,.\"\'()[]{}!:;&";
    
    std::fstream fs;
    fs.open(filename, std::fstream::in);
    if(!fs.good()) {
        cerr << ">> fstream open file error" << endl;
        return false;
    }
    char buffer[buffersize];

    char* saveptr = nullptr;
    int i = 0;
    while (fs.getline(buffer, sizeof(buffer) - 1).good()) {
        auto token = strtok_r(buffer, delims, &saveptr);
        while (token != nullptr) {
            // words.emplace_back(token);
            words[i++] = token;
            token = strtok_r(nullptr, delims, &saveptr);
        }
    }
    
    fs.close();
    return true;
}

void InternWords(const string words[]) {
    int cnt = 0;
    clock_t t1 = clock();
    for (int i = 0; i < internsize; ++i) {
        auto ref = intern->Add(words[i]);
        refs[i] = ref;
        std::string str = intern->ToString(ref);
    }
    clock_t t2 = clock();
    std::cout << "Intern time_used: " << (t2 - t1) / (CLOCKS_PER_SEC / 1000) << " millisecond(s)" << std::endl;
}

void InternFile(const char* filename) {
    // vector<string> words;
    string* words = new string[internsize];
    GetFileWords(filename, words);
    std::cout << "words.size() = " << internsize << std::endl;

    InternWords(words);
    
    delete[] words;
}

bool GetAccessSequence(const char* filename, int seqs[]) {
    std::fstream fs;
    fs.open(filename, std::fstream::in);
    if(!fs.good()) {
        cerr << ">> fstream open file error" << endl;
        return false;
    }
    char buf[100];
    int seq;
    int i = 0;
    while(fs.getline(buf, sizeof(buf) - 1).good()) {
        seq = atoi(buf);
        seqs[i++] = seq;
    }
    return true;
}

void TestInternTimeUsed(const char* file)
{
    InternFile(file);
    std::cout << file << ", pages = " << intern->GetPageCount() << ", entries = " << intern->GetEntryCount() << std::endl;
}

void TestGetStringTimeUsed(int times)
{
    // vector<int> seqs;
    int* seqs = new int[times];
    string seqfilename = "TestFiles/testseq/test_seq_" + to_string(times) + ".txt";
    GetAccessSequence(seqfilename.c_str(), seqs);
    std::cout << "access_seqs.size() = " << times << std::endl;

    clock_t t1 = clock();
    // for(auto seq : seqs) {
    for(int i = 0; i < times; ++i) {
        // string str = intern->ToString(refs[seq]);
        intern->ToString(refs[seqs[i]]);
    }
    clock_t t2 = clock();
    
    delete[] seqs;

    std::cout << "GetString time_used: " << (t2 - t1) / (CLOCKS_PER_SEC / (1000 * 1)) << " millisecond(s)" << std::endl;
}

void TestMemUsed()
{
    HANDLE handle = GetCurrentProcess();
    PROCESS_MEMORY_COUNTERS pmc;
    GetProcessMemoryInfo(handle, &pmc, sizeof(pmc));

    std::cout << "Memery used: " << pmc.WorkingSetSize/(1024*1024) - 3 << " MB" << std::endl;  //结果单位是Byte，3MB是公共开销，通过helloworld程序测试得
}

void TestDelInternStringTimeUsed()
{
    clock_t t1 = clock();
    delete intern;
    intern = nullptr;
    delete[] refs;
    refs = nullptr;
    clock_t t2 = clock();
    std::cout << "DelIntern time_used: " << (t2 - t1) / (CLOCKS_PER_SEC / (1000 * 1)) << " millisecond(s)" << std::endl;
}

void test(string file)
{    
    refs = new rs::stringintern::StringReference[1000000];
    intern = new rs::stringintern::StringIntern();
    std::cout << std::endl << "Test started" << std::endl;
    TestInternTimeUsed(file.c_str());
    TestGetStringTimeUsed(10);
    TestMemUsed();
    TestDelInternStringTimeUsed();
}

int main(int argv, char** args) 
{
    // int in = internsize / 10000;
    int byte[] = {8, 16, 32, 64, 128};
    for(int a : byte) {
        string file = "TestFiles/finaltest/test_emulated_50%_" + to_string(internsize/10000) + "w_" + to_string(a) + ".txt";
        test(file);
    }

    system("pause");
}